﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krile.Kernel.Data;
using Krile.Kernel.Bridges;

namespace Krile.Cores
{
    public class KeyAssign : IKeyAssign
    {
        public KeyAssign()
        {
            assignDatas = new Dictionary<Keys, Krile.Cores.KeyAssignElems.KeyAssignData>();
            candidates = new List<Krile.Cores.KeyAssignElems.KeyAssignCallbackCandidate>();
        }

        public void SetKeyEvent(Form form)
        {
            if (!(form is IForm))
                throw new ArgumentException("argument form should be castable to IForm.");
            form.KeyPreview = true;
            form.PreviewKeyDown += new PreviewKeyDownEventHandler(form_PreviewKeyDown);
            form.KeyDown += new KeyEventHandler(form_KeyDown);
        }

        void form_KeyDown(object sender, KeyEventArgs e)
        {
            var tgctrl = sender as Control;
            while (sender != null && (!(sender is Form) || !(sender is IForm)))
            {
                tgctrl = tgctrl.Parent;
            }

            if (tgctrl == null)
                return;
            var tform = tgctrl as Form;
            var lookup = LookupAssignData(e.KeyData);
            if (lookup != null)
            {
                if (
                    (
                    Krile.Imported.Snippets.WinForms.GetDeepestActiveControl(tform).GetType() == typeof(TextBox) ||
                    Krile.Imported.Snippets.WinForms.GetDeepestActiveControl(tform).GetType().IsSubclassOf(typeof(TextBox)) ||
                    Krile.Imported.Snippets.WinForms.GetDeepestActiveControl(tform).GetType() == typeof(RichTextBox) ||
                    Krile.Imported.Snippets.WinForms.GetDeepestActiveControl(tform).GetType().IsSubclassOf(typeof(RichTextBox))
                    ) &&
                    !lookup.EnableOnTextBox)
                    return;
                e.Handled = true;
                e.SuppressKeyPress = true;
                if (Core.FormManager.MainForm.PreviousForegroundForm != null &&
                    Core.FormManager.MainForm.PreviousForegroundForm.SelectedStatus != null)
                    lookup.InvokeAll((IForm)tform, Core.FormManager.MainForm.PreviousForegroundForm.SelectedStatus, e.KeyData);
                else
                    lookup.InvokeAll((IForm)tform, null, e.KeyData);
            }
        }

        void form_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                case Keys.Enter:
                case Keys.Escape:
                case Keys.ShiftKey:
                    e.IsInputKey = true;
                    break;
            }
        }

        /// <summary>
        /// Key assign dict
        /// </summary>
        Dictionary<Keys, KeyAssignElems.KeyAssignData> assignDatas;

        /// <summary>
        /// Defreeze assign datas
        /// </summary>
        internal void DefreezeAssigns()
        {
            foreach (var adata in assignDatas.Values)
            {
                adata.DefreezeAll();
            }
        }

        /// <summary>
        /// Key assign candidate elements
        /// </summary>
        List<KeyAssignElems.KeyAssignCallbackCandidate> candidates;

        /// <summary>
        /// Get candidate array
        /// </summary>
        public KeyAssignElems.KeyAssignCallbackCandidate[] GetCandidatesArray()
        {
            return candidates.ToArray();
        }

        /// <summary>
        /// Get assign data array(for external memento pattern)
        /// </summary>
        internal KeyAssignElems.KeyAssignData[] AssignDataArray
        {
            get
            {
                List<KeyAssignElems.KeyAssignData> rets = new List<Krile.Cores.KeyAssignElems.KeyAssignData>();
                foreach (var val in assignDatas.Values)
                {
                    rets.Add(val);
                }
                return rets.ToArray();
            }
            set
            {
                assignDatas.Clear();
                if (value == null) return;
                foreach (var val in value)
                {
                    assignDatas.Add(val.LinkedKeys, val);
                }
            }
        }

        /// <summary>
        /// Lookup key assign data from key
        /// </summary>
        /// <param name="key">key</param>
        /// <returns>assign data or null</returns>
        internal KeyAssignElems.KeyAssignData LookupAssignData(Keys key)
        {
            if (assignDatas.ContainsKey(key))
                return assignDatas[key];
            else
                return null;
        }

        /// <summary>
        /// Lookup callback method
        /// </summary>
        /// <param name="clsname">class name</param>
        /// <param name="method">method</param>
        /// <returns>method or null</returns>
        public Cores.KeyAssignElems.KeyAssignCallbackCandidate LookupCallback(string clsname, string method)
        {
            foreach (var candidate in candidates)
            {
                if (((candidate.CallbackMethod.Target == null && clsname == null) ||
                    (candidate.CallbackMethod.Target != null && candidate.CallbackMethod.Target.GetType().FullName == clsname)) &&
                    candidate.CallbackMethod.Method.Name == method)
                {
                    return candidate;
                }
            }
            return null;
        }

        /// <summary>
        /// Get description of method
        /// </summary>
        /// <param name="callbackMethod">target method</param>
        /// <returns>description string or null</returns>
        public string GetDescription(Krile.Kernel.Data.Common.KeyAssignCallback callbackMethod)
        {
            foreach (var candidate in candidates)
            {
                if (candidate.CallbackMethod == callbackMethod)
                {
                    return candidate.Description;
                }
            }
            return null;
        }

        /// <summary>
        /// Add key assign candidate method
        /// </summary>
        /// <param name="addMethod">adding method</param>
        /// <param name="description">method's description</param>
        public void AddAssignCandidateMethod(Krile.Kernel.Data.Common.KeyAssignCallback addMethod, string description)
        {
            candidates.Add(new KeyAssignElems.KeyAssignCallbackCandidate(addMethod, description));
        }
    }
}
